package cn.line5.seam.intercept;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;

import cn.line5.seam.annotations.In;
import cn.line5.seam.annotations.Transaction;
import cn.line5.seam.cache.ComponentCache;
import cn.line5.seam.cache.DBConfigCache;
import cn.line5.seam.cache.InterceptorCache;
import cn.line5.seam.contexts.JdbcContext;
import cn.line5.seam.exception.SeamException;
import cn.line5.seam.util.ReflectUtil;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * <h1>拦截器处理类</h1>
 * 
 * @author Liu,Xiaojun
 * @since JDK1.6
 */
public class BaseInterceptor implements MethodInterceptor {
	private Object target;
	private Method method;
	private Object[] args;

	/**
	 * 构造方法
	 * 
	 * @param target
	 *            对象类
	 */
	public BaseInterceptor(Object target) {
		this.target = target;
	}

//	@Override
	/**
	 * 覆盖MethodInterceptor的intercept方法，该方法不允许再被覆盖。
	 */
	public final Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Exception {
		this.method = method;
		this.args = args;
		return arroundInvork();
	}

	/**
	 * 允许被覆盖的方法，可以自定义拦截事件
	 * 
	 * @return 方法执行的返回值
	 * @throws Exception
	 *             方法执行过程中发生异常
	 */
	protected Object arroundInvork() throws Exception {
		return proess();
	}

	/**
	 * 方法执行，如果覆盖了arroundInvork方法，在arroundInvork方法中必须调用该方法。
	 * 
	 * @return 方法执行的返回值
	 * @throws Exception
	 *             方法执行过程中发生异常
	 */
	protected final Object proess() throws Exception {
		Transaction t = this.method.getAnnotation(Transaction.class);
		boolean closeConn = false;// 是否强制关闭连接
		String methodInvoke = this.target + "." + this.method.getName();
		if (JdbcContext.getMethodInvoke() == null || JdbcContext.getMethodInvoke().length() <= 0)
			// 组装调用方法名称并写入线程池中去
			JdbcContext.setMethodInvoke(methodInvoke);
		Connection conn = JdbcContext.getConnection();
		boolean autoCommit = DBConfigCache.autoCommit;
		if (t != null && conn != null) {// 如果方法要求启用事务，设置自动提交为false
			autoCommit = conn.getAutoCommit();
			conn.setAutoCommit(false);
		}
		Object obj = null;
		try {
			obj = this.method.invoke(this.target, this.args);
			if (t != null && conn != null)// 如果方法要求启用事务，正常下提交
				conn.commit();
		} catch (RuntimeException e) {
			if (t != null && conn != null) {// 如果方法要求启用事务，异常下回滚并设置强行关闭连接
				closeConn = true;
				conn.rollback();
			}
			throw new SeamException("The method running error named '"
					+ method.getName() + "' in the class '"
					+ this.target.getClass().getName() + "'.");
		} finally {
			if (t != null && conn != null)// 如果方法要求启用事务
				conn.setAutoCommit(autoCommit);
			if (closeConn || methodInvoke.equals(JdbcContext.getMethodInvoke())) {// 最外层调用方法或者强制关闭连接
				JdbcContext.closeConnection();
				JdbcContext.clearMethodInvoke();
			}
		}
		return obj;
	}

	/**
	 * 创建拦截器机制对象
	 * 
	 * @param target
	 *            对象类
	 * @return 拦截器机制对象
	 * @throws Exception
	 *             创建拦截器机制对象过程中发生异常
	 */
	public static Object createCglibProxy(Object target) throws Exception {
		Class<?> clazz = Class.forName(InterceptorCache.interceptorClass);
		BaseInterceptor proxy = (BaseInterceptor) clazz.getConstructor(
				new Class[] { Object.class }).newInstance(target);
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			// 对拦截器的In注解字段进行注入
			In in = field.getAnnotation(In.class);
			if (in != null)
				ReflectUtil.setFieldValue(field.getName(), proxy,
						ComponentCache.componentClass.get(in.value())
								.getComponentInterceptor());
		}
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(proxy);
		return enhancer.create();
	}
}
